package org.cainiao.api.lark.impl.util.converter;

import lombok.Setter;
import org.cainiao.api.lark.dto.response.LarkDataResponse;
import org.cainiao.api.lark.dto.response.LarkResponse;
import org.cainiao.api.lark.dto.response.ShortcutInfo;
import org.cainiao.api.lark.dto.response.authenticateandauthorize.getaccesstokens.AppAccessTokenResponse;
import org.cainiao.api.lark.dto.response.authenticateandauthorize.getaccesstokens.LarkTokenInfo;
import org.cainiao.api.lark.dto.response.docs.docs.apireference.document.*;
import org.cainiao.api.lark.dto.response.docs.docs.apireference.document.text.*;
import org.cainiao.api.lark.dto.response.docs.space.folder.LarkFile;
import org.cainiao.api.lark.dto.response.docs.space.folder.LarkFilePage;
import org.cainiao.api.lark.dto.response.docs.space.folder.LarkFolderMeta;

import java.util.Map;
import java.util.function.Function;

import static org.cainiao.api.lark.dto.response.LarkResponse.LarkError;
import static org.cainiao.common.util.MapUtil.CnMap;

/**
 * 转换接口结果<br />
 * 将飞书开放接口响应中的字段转换为对象
 * <p>
 * Author: Cai Niao(wdhlzd@163.com)<br />
 */
@Setter
public class LarkFieldNameMapResponseConverter implements ResponseConverter {

    @Override
    public AppAccessTokenResponse convertToAppAccessTokenResponse(Map<String, Object> responseMap) {
        CnMap responseBody = new CnMap(responseMap);
        AppAccessTokenResponse appAccessTokenResponse = AppAccessTokenResponse.builder()
            .expire(responseBody.getInteger("expire"))
            .appAccessToken(responseBody.getString("app_access_token"))
            .build();
        setLarkResponse(responseBody, appAccessTokenResponse);
        return appAccessTokenResponse;
    }

    @Override
    public LarkDataResponse<LarkTokenInfo> convertToLarkTokenInfoResponse(Map<String, Object> responseMap) {
        return getLarkDataResponse(responseMap, this::convertToLarkTokenInfo);
    }

    protected LarkTokenInfo convertToLarkTokenInfo(CnMap tokenInfoData) {
        return LarkTokenInfo.builder()
            .accessToken(tokenInfoData.getString("access_token"))
            .refreshToken(tokenInfoData.getString("refresh_token"))
            .tokenType(tokenInfoData.getString("token_type"))
            .expiresIn(tokenInfoData.getInteger("expires_in"))
            .refreshExpiresIn(tokenInfoData.getInteger("refresh_expires_in"))
            .scope(tokenInfoData.getString("scope"))
            .build();
    }

    @Override
    public LarkDataResponse<LarkFolderMeta> convertToLarkFolderMetaResponse(Map<String, Object> responseMap) {
        return getLarkDataResponse(responseMap, this::convertToLarkFolderMeta);
    }

    protected LarkFolderMeta convertToLarkFolderMeta(CnMap folderMetaData) {
        return LarkFolderMeta.builder()
            .id(folderMetaData.getString("id"))
            .name(folderMetaData.getString("name"))
            .token(folderMetaData.getString("token"))
            .createUid(folderMetaData.getString("createUid"))
            .editUid(folderMetaData.getString("editUid"))
            .parentId(folderMetaData.getString("parentId"))
            .ownUid(folderMetaData.getString("ownUid"))
            .build();
    }

    @Override
    public LarkDataResponse<LarkFilePage> convertToLarkFilePageResponse(Map<String, Object> responseMap) {
        return getLarkDataResponse(responseMap, this::convertToLarkFilePage);
    }

    protected LarkFilePage convertToLarkFilePage(CnMap filePageData) {
        return LarkFilePage.builder()
            .files(filePageData.getListByMapConverter("files", this::convertToLarkFile))
            .nextPageToken(filePageData.getString("next_page_token"))
            .hasMore(filePageData.getBoolean("has_more"))
            .build();
    }

    protected LarkFile convertToLarkFile(CnMap fileData) {
        return LarkFile.builder()
            .token(fileData.getString("token"))
            .name(fileData.getString("name"))
            .type(fileData.getString("type"))
            .parentToken(fileData.getString("parent_token"))
            .url(fileData.getString("url"))
            .shortcutInfo(fileData.getByMapConverter("shortcut_info", this::convertToShortcutInfo))
            .createdTime(fileData.getString("created_time"))
            .modifiedTime(fileData.getString("modified_time"))
            .ownerId(fileData.getString("owner_id"))
            .build();
    }

    protected ShortcutInfo convertToShortcutInfo(CnMap shortcutInfoData) {
        return ShortcutInfo.builder()
            .targetType(shortcutInfoData.getString("target_type"))
            .targetToken(shortcutInfoData.getString("target_token"))
            .build();
    }

    @Override
    public LarkDataResponse<LarkBlockPage> convertToLarkBlockPageResponse(Map<String, Object> responseMap) {
        return getLarkDataResponse(responseMap, this::convertToLarkBlockPage);
    }

    protected LarkBlockPage convertToLarkBlockPage(CnMap blockPageData) {
        return LarkBlockPage.builder()
            .items(blockPageData.getListByMapConverter("items", this::convertToLarkBlock))
            .pageToken(blockPageData.getString("page_token"))
            .hasMore(blockPageData.getBoolean("has_more"))
            .build();
    }

    protected LarkBlock convertToLarkBlock(CnMap blockData) {
        return LarkBlock.builder()
            .blockId(blockData.getString("block_id"))
            .parentId(blockData.getString("parent_id"))
            .children(blockData.getListByObjectConverter("children", Object::toString))
            .blockType(blockData.getInt("block_type"))
            .page(blockData.getByMapConverter("page", this::convertToLarkText))
            .text(blockData.getByMapConverter("text", this::convertToLarkText))
            .heading1(blockData.getByMapConverter("heading1", this::convertToLarkText))
            .heading2(blockData.getByMapConverter("heading2", this::convertToLarkText))
            .heading3(blockData.getByMapConverter("heading3", this::convertToLarkText))
            .heading4(blockData.getByMapConverter("heading4", this::convertToLarkText))
            .heading5(blockData.getByMapConverter("heading5", this::convertToLarkText))
            .heading6(blockData.getByMapConverter("heading6", this::convertToLarkText))
            .heading7(blockData.getByMapConverter("heading7", this::convertToLarkText))
            .heading8(blockData.getByMapConverter("heading8", this::convertToLarkText))
            .heading9(blockData.getByMapConverter("heading9", this::convertToLarkText))
            .bullet(blockData.getByMapConverter("bullet", this::convertToLarkText))
            .ordered(blockData.getByMapConverter("ordered", this::convertToLarkText))
            .code(blockData.getByMapConverter("code", this::convertToLarkText))
            .quote(blockData.getByMapConverter("quote", this::convertToLarkText))
            .equation(blockData.getByMapConverter("equation", this::convertToLarkText))
            .todo(blockData.getByMapConverter("todo", this::convertToLarkText))
            .bitable(blockData.getByMapConverter("bitable", this::convertToLarkBitable))
            .callout(blockData.getByMapConverter("callout", this::convertToLarkCallout))
            .chatCard(blockData.getByMapConverter("chat_card", this::convertToLarkChatCard))
            .diagram(blockData.getByMapConverter("diagram", this::convertToLarkDiagram))
            .file(blockData.getByMapConverter("file", this::convertToLarkFileBlock))
            .grid(blockData.getByMapConverter("grid", this::convertToLarkGrid))
            .gridColumn(blockData.getByMapConverter("grid_column", this::convertToLarkGridColumn))
            // TODO
            .board(blockData.getByMapConverter("board", this::convertToLarkBoard))
            .build();
    }

    protected LarkGridColumn convertToLarkGridColumn(CnMap larkGridColumnData) {
        return LarkGridColumn.builder().widthRatio(larkGridColumnData.getInt("width_ratio")).build();
    }

    protected LarkGrid convertToLarkGrid(CnMap larkGridData) {
        return LarkGrid.builder().columnSize(larkGridData.getInt("column_size")).build();
    }

    protected LarkBoard convertToLarkBoard(CnMap larkBoardData) {
        return LarkBoard.builder()
            .token(larkBoardData.getString("token"))
            .align(larkBoardData.getInteger("align"))
            .width(larkBoardData.getInteger("width"))
            .height(larkBoardData.getInteger("height"))
            .build();
    }

    protected LarkFileBlock convertToLarkFileBlock(CnMap fileBlockData) {
        return LarkFileBlock.builder()
            .token(fileBlockData.getString("token"))
            .name(fileBlockData.getString("name"))
            .viewType(fileBlockData.getInt("view_type"))
            .build();
    }

    protected LarkBitable convertToLarkBitable(CnMap bitableData) {
        return LarkBitable.builder().token(bitableData.getString("token")).build();
    }

    protected LarkCallout convertToLarkCallout(CnMap calloutData) {
        return LarkCallout.builder()
            .backgroundColor(calloutData.getInt("background_color"))
            .borderColor(calloutData.getInt("border_color"))
            .textColor(calloutData.getInteger("text_color"))
            .emojiId(calloutData.getString("emoji_id"))
            .build();
    }

    protected LarkChatCard convertToLarkChatCard(CnMap chatCardData) {
        return LarkChatCard.builder()
            .chatId(chatCardData.getString("chat_id"))
            .align(chatCardData.getInt("align"))
            .build();
    }

    protected LarkDiagram convertToLarkDiagram(CnMap diagramData) {
        return LarkDiagram.builder().diagramType(diagramData.getInt("diagram_type")).build();
    }

    protected LarkText convertToLarkText(CnMap textData) {
        return LarkText.builder()
            .style(textData.getByMapConverter("style", this::convertToLarkTextStyle))
            .elements(textData.getListByMapConverter("elements", this::convertToLarkTextElement))
            .build();
    }

    protected LarkTextStyle convertToLarkTextStyle(CnMap textStyleData) {
        return LarkTextStyle.builder()
            .align(textStyleData.getInteger("align"))
            .done(textStyleData.getBoolean("done"))
            .folded(textStyleData.getBoolean("folded"))
            .language(textStyleData.getInteger("language"))
            .wrap(textStyleData.getBoolean("wrap"))
            .backgroundColor(textStyleData.getString("background_color"))
            .indentationLevel(textStyleData.getString("indentation_level"))
            .sequence(textStyleData.getString("sequence"))
            .build();
    }

    protected LarkTextElement convertToLarkTextElement(CnMap textElementData) {
        return LarkTextElement.builder()
            .textRun(textElementData.getByMapConverter("text_run", this::convertToLarkTextRun))
            .mentionUser(textElementData.getByMapConverter("mention_user", this::convertToLarkMentionUser))
            .mentionDoc(textElementData.getByMapConverter("mention_doc", this::convertToLarkMentionDoc))
            .reminder(textElementData.getByMapConverter("reminder", this::convertToLarkReminder))
            .file(textElementData.getByMapConverter("file", this::convertToLarkInlineFile))
            .undefined(textElementData.getByMapConverter("undefined", this::convertToLarkUndefinedElement))
            .inlineBlock(textElementData.getByMapConverter("inline_block", this::convertToLarkInlineBlock))
            .equation(textElementData.getByMapConverter("equation", this::convertToLarkEquation))
            .build();
    }

    protected LarkTextRun convertToLarkTextRun(CnMap textRunData) {
        return LarkTextRun.builder()
            .content(textRunData.getString("content"))
            .textElementStyle(getLarkTextElementStyle(textRunData))
            .build();
    }

    protected LarkMentionUser convertToLarkMentionUser(CnMap mentionUserData) {
        return LarkMentionUser.builder()
            .userId(mentionUserData.getString("user_id"))
            .textElementStyle(getLarkTextElementStyle(mentionUserData))
            .build();
    }

    protected LarkMentionDoc convertToLarkMentionDoc(CnMap mentionDocData) {
        return LarkMentionDoc.builder()
            .token(mentionDocData.getString("token"))
            .objType(mentionDocData.getInt("obj_type"))
            .url(mentionDocData.getString("url"))
            .title(mentionDocData.getString("title"))
            .textElementStyle(getLarkTextElementStyle(mentionDocData))
            .build();
    }

    protected LarkReminder convertToLarkReminder(CnMap reminderData) {
        return LarkReminder.builder()
            .createUserId(reminderData.getString("create_user_id"))
            .isNotify(reminderData.getBoolean("is_notify"))
            .isWholeDay(reminderData.getBoolean("is_whole_day"))
            .expireTime(reminderData.getString("expire_time"))
            .notifyTime(reminderData.getString("notify_time"))
            .textElementStyle(getLarkTextElementStyle(reminderData))
            .build();
    }

    protected LarkInlineFile convertToLarkInlineFile(CnMap inlineFileData) {
        return LarkInlineFile.builder()
            .fileToken(inlineFileData.getString("file_token"))
            .sourceBlockId(inlineFileData.getString("source_block_id"))
            .textElementStyle(getLarkTextElementStyle(inlineFileData))
            .build();
    }

    protected LarkUndefinedElement convertToLarkUndefinedElement(CnMap undefinedElementData) {
        return LarkUndefinedElement.builder().build();
    }

    protected LarkInlineBlock convertToLarkInlineBlock(CnMap inlineBlockData) {
        return LarkInlineBlock.builder()
            .blockId(inlineBlockData.getString("block_id"))
            .textElementStyle(getLarkTextElementStyle(inlineBlockData))
            .build();
    }

    protected LarkEquation convertToLarkEquation(CnMap equationData) {
        return LarkEquation.builder()
            .content(equationData.getString("content"))
            .textElementStyle(getLarkTextElementStyle(equationData))
            .build();
    }

    protected LarkTextElementStyle convertToLarkTextElementStyle(CnMap textElementStyleData) {
        return LarkTextElementStyle.builder()
            .bold(textElementStyleData.getBoolean("bold"))
            .italic(textElementStyleData.getBoolean("italic"))
            .strikethrough(textElementStyleData.getBoolean("strikethrough"))
            .underline(textElementStyleData.getBoolean("underline"))
            .inlineCode(textElementStyleData.getBoolean("inline_code"))
            .backgroundColor(textElementStyleData.getInteger("background_color"))
            .textColor(textElementStyleData.getInteger("text_color"))
            .link(textElementStyleData.getByMapConverter("link", this::convertToLarkLink))
            .commentIds(textElementStyleData.getStringList("comment_ids"))
            .build();
    }

    protected LarkLink convertToLarkLink(CnMap linkData) {
        return LarkLink.builder().url(linkData.getString("url")).build();
    }

    private <R> LarkDataResponse<R> getLarkDataResponse(Map<String, Object> body,
                                                        Function<CnMap, R> transferData) {
        CnMap responseBody = new CnMap(body);
        LarkDataResponse<R> larkDataResponse = new LarkDataResponse<>();
        setLarkResponse(responseBody, larkDataResponse);
        CnMap data = responseBody.getCnMap("data");
        if (data != null) {
            larkDataResponse.setData(transferData.apply(data));
        }
        return larkDataResponse;
    }

    private void setLarkResponse(CnMap responseBody, LarkResponse larkResponse) {
        larkResponse.setCode(responseBody.getInt("code"));
        larkResponse.setMsg(responseBody.getString("msg"));
        Object errorObject = responseBody.get("error");
        if (errorObject != null) {
            larkResponse.setError(LarkError.builder().logId("log_id").build());
        }
    }

    private LarkTextElementStyle getLarkTextElementStyle(CnMap data) {
        return data.getByMapConverter("text_element_style", this::convertToLarkTextElementStyle);
    }
}
